#include "desencrypt_p.h"
#include <cryptopp/base64.h>
#include <cryptopp/des.h>
#include <cryptopp/filters.h>
#include <cryptopp/md5.h>
#include <cryptopp/modes.h>
#include <iomanip>
#include <random>
#include <sstream>

std::string DESEncryptPrivate::encrypt(const std::string& plaintext)
{
	const auto key = createKey(KEY);
	const auto iv = createIV(KEY);

	const std::vector<uint8_t> plainBytes(plaintext.begin(), plaintext.end());

	const auto cipherBytes = encryptDES_CBC(plainBytes, key, iv);

	return bytesToHex(cipherBytes);
}

std::string DESEncryptPrivate::decrypt(const std::string& ciphertext)
{
	const auto key = createKey(KEY);
	const auto iv = createIV(KEY);

	const std::vector<uint8_t> cipherBytes = hexToBytes(ciphertext);

	const auto plainBytes = decryptDES_CBC(cipherBytes, key, iv);

	return std::string(plainBytes.begin(), plainBytes.end());
}
std::string DESEncryptPrivate::encrypt(const std::string& plaintext, const std::string& keyString)
{
	const auto key = createKey(keyString);
	const auto iv = createIV(keyString);

	const std::vector<uint8_t> plainBytes(plaintext.begin(), plaintext.end());

	const auto cipherBytes = encryptDES_CBC(plainBytes, key, iv);

	return bytesToHex(cipherBytes);
}
std::string DESEncryptPrivate::decrypt(const std::string& ciphertext, const std::string& keyString)
{
	const auto key = createKey(keyString);
	const auto iv = createIV(keyString);

	const std::vector<uint8_t> cipherBytes = hexToBytes(ciphertext);

	const auto plainBytes = decryptDES_CBC(cipherBytes, key, iv);

	return std::string(plainBytes.begin(), plainBytes.end());
}
std::string DESEncryptPrivate::base64Encrypt(const std::string& plaintext)
{
	using namespace CryptoPP;

	// 1️⃣ Base64 编码
	std::string encoded;
	StringSource(plaintext, true,
				 new Base64Encoder(
						 new StringSink(encoded),
						 false// 不添加换行符
						 ));

	// 2️⃣ 生成 6 个随机大写字母前缀
	std::random_device rd;
	std::mt19937 gen(rd());
	std::uniform_int_distribution<int> dist(0, 25);

	std::string prefix;
	for (int i = 0; i < 6; ++i)
	{
		char c = 'A' + dist(gen);
		prefix += c;
	}

	// 3️⃣ 拼接前缀
	return prefix + encoded;
}
std::string DESEncryptPrivate::bytesToHex(const std::vector<uint8_t>& data)
{
	std::ostringstream oss;
	oss << std::hex << std::uppercase;
	for (uint8_t byte: data)
	{
		oss << std::setw(2) << std::setfill('0') << static_cast<int>(byte);
	}
	return oss.str();
}
std::vector<uint8_t> DESEncryptPrivate::hexToBytes(const std::string& hex)
{
	if (hex.size() % 2 != 0)
	{
		throw std::invalid_argument("Hex string length must be even");
	}

	std::vector<uint8_t> bytes;
	bytes.reserve(hex.size() / 2);

	for (size_t i = 0; i < hex.size(); i += 2)
	{
		char high = hex[i];
		char low = hex[i + 1];

		auto hexCharToInt = [](char c) -> uint8_t {
			if (c >= '0' && c <= '9') return c - '0';
			else if (c >= 'A' && c <= 'F')
				return c - 'A' + 10;
			else if (c >= 'a' && c <= 'f')
				return c - 'a' + 10;
			else
				throw std::invalid_argument("Invalid hex character");
		};

		uint8_t byte = (hexCharToInt(high) << 4) | hexCharToInt(low);
		bytes.push_back(byte);
	}

	return bytes;
}
std::vector<uint8_t> DESEncryptPrivate::createKey(const std::string& keyString)
{
	using namespace CryptoPP;

	// 1. 计算 MD5 原始字节
	CryptoPP::byte digest[CryptoPP::MD5::DIGESTSIZE];
	CryptoPP::MD5 hash;
	hash.CalculateDigest(digest, reinterpret_cast<const CryptoPP::byte*>(keyString.data()), keyString.size());

	// 2. 转 HEX 字符串
	std::ostringstream oss;
	oss << std::hex << std::uppercase;
	for (int i = 0; i < 16; ++i) oss << std::setw(2) << std::setfill('0') << static_cast<int>(digest[i]);
	std::string md5Hex = oss.str();  // 32字符 HEX

	// 3. 取前 8 个字符作为 ASCII
	std::vector<uint8_t> key(md5Hex.begin(), md5Hex.begin() + 8);
	return key;
}
std::vector<uint8_t> DESEncryptPrivate::createIV(const std::string& ivString)
{
	return createKey(ivString);
}
std::vector<uint8_t> DESEncryptPrivate::encryptDES_CBC(const std::vector<uint8_t>& plainBytes, const std::vector<uint8_t>& key, const std::vector<uint8_t>& iv)
{
	using namespace CryptoPP;

	if (key.size() != DES::DEFAULT_KEYLENGTH || iv.size() != DES::BLOCKSIZE)
	{
		throw std::runtime_error("Key or IV length is invalid for DES");
	}

	DES::Encryption desEncryption;
	desEncryption.SetKey(reinterpret_cast<const byte*>(key.data()), key.size());

	CBC_Mode_ExternalCipher::Encryption cbc(desEncryption,
											reinterpret_cast<const byte*>(iv.data()),
											iv.size());

	std::vector<uint8_t> encryptedBytes;

	ArraySource(plainBytes.data(), plainBytes.size(), true,
				new StreamTransformationFilter(cbc,
											   new VectorSink(encryptedBytes),
											   StreamTransformationFilter::PKCS_PADDING));

	return encryptedBytes;
}

std::vector<uint8_t> DESEncryptPrivate::decryptDES_CBC(const std::vector<uint8_t>& cipherBytes, const std::vector<uint8_t>& key, const std::vector<uint8_t>& iv)
{
	using namespace CryptoPP;

	if (key.size() != DES::DEFAULT_KEYLENGTH || iv.size() != DES::BLOCKSIZE)
		throw std::runtime_error("Key or IV length is invalid for DES");

	DES::Decryption desDecryption;
	desDecryption.SetKey(key.data(), key.size());

	CBC_Mode_ExternalCipher::Decryption cbc(desDecryption, iv.data(), iv.size());

	std::vector<uint8_t> decryptedBytes;

	ArraySource(cipherBytes.data(), cipherBytes.size(), true,
				new StreamTransformationFilter(cbc,
											   new VectorSink(decryptedBytes),
											   StreamTransformationFilter::PKCS_PADDING));

	return decryptedBytes;
}